use postgres::Client;
use test_common::*;

const FN_QUERY: &str = r#"
WITH schema_visibility AS (
    SELECT * FROM (VALUES
      ('prom_api', true)
    , ('prom_info', true)
    , ('prom_metric', true)
    , ('ps_tag', true)
    , ('ps_trace', true)
    , ('_prom_catalog', false)
    , ('_ps_catalog', false)
    , ('_prom_ext', false)
    , ('_ps_trace', false)
    )
    AS v (schema, public)
)
SELECT
    CASE p.prokind
        WHEN 'a' THEN 'aggregate'
        WHEN 'w' THEN 'window'
        WHEN 'p' THEN 'procedure'
        ELSE 'function'
        END as type
     , pg_namespace.nspname as schema
     , p.proname as name
     , COALESCE(pg_catalog.pg_get_function_result(p.oid), 'void') as return_type
     , COALESCE(pg_catalog.pg_get_function_arguments(p.oid), '') as arguments
     , COALESCE(obj_description(p.oid, 'pg_proc'), '') as description
FROM pg_catalog.pg_proc p
         JOIN pg_catalog.pg_namespace ON pg_namespace.oid = p.pronamespace
         INNER JOIN schema_visibility ON schema_visibility.schema = pg_namespace.nspname
ORDER BY schema_visibility.public DESC, pg_namespace.nspname, p.proname, pg_catalog.pg_get_function_arguments(p.oid);
"#;

const OP_QUERY: &str = r#"
WITH schema_visibility AS (
    SELECT * FROM (VALUES
      ('prom_api', true)
    , ('prom_info', true)
    , ('prom_metric', true)
    , ('ps_tag', true)
    , ('ps_trace', true)
    , ('_prom_catalog', false)
    , ('_ps_catalog', false)
    , ('_prom_ext', false)
    , ('_ps_trace', false)
    ) AS v (schema, public)
)
SELECT n.nspname AS schema,
       o.oprname AS name,
       CASE WHEN o.oprkind='l' THEN NULL ELSE pg_catalog.format_type(o.oprleft, NULL) END AS left_arg_type,
       CASE WHEN o.oprkind='r' THEN NULL ELSE pg_catalog.format_type(o.oprright, NULL) END AS right_arg_type,
       pg_catalog.format_type(o.oprresult, NULL) AS result_type,
       o.oprcode::TEXT AS function,
       COALESCE(
           COALESCE(
               pg_catalog.obj_description(o.oid, 'pg_operator'),
               pg_catalog.obj_description(o.oprcode, 'pg_proc')
           )
       , '')
      AS description
FROM pg_catalog.pg_operator o
    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace
    INNER JOIN schema_visibility ON schema_visibility.schema = n.nspname
ORDER BY schema_visibility.public DESC, schema, name, left_arg_type, right_arg_type;
"#;

fn main() {
    let pg_blueprint = PostgresContainerBlueprint::new();
    let test_instance = test_common::new_test_instance_from_env(&pg_blueprint);
    let mut test_connection = test_instance.connect();
    dump_sql_from_connection(&mut test_connection.client);
}

fn dump_sql_from_connection(connection: &mut Client) {
    connection
        .simple_query("CREATE EXTENSION promscale;")
        .unwrap();
    println!("# SQL API");
    println!("<!-- Note: This document is autogenerated. DO NOT EDIT. -->");
    output_functions(connection);
    output_operators(connection);
}

/// Given a connection to a database, output a GitHub-Flavored Markdown table of functions
fn output_functions(connection: &mut Client) {
    println!("## Functions");
    let results = connection.query(FN_QUERY, &[]).unwrap();
    for row in results {
        let schema: &str = row.get("schema");
        let name: &str = row.get("name");
        println!("### {}.{}", schema, name);
        let return_type: &str = row.get("return_type");
        let arguments: &str = row.get("arguments");
        let ty: &str = row.get("type");
        let description: &str = row.get("description");
        println!("{}", description);
        println!("```");
        println!(
            "{} {} **{}.{}**({})",
            ty, return_type, schema, name, arguments
        );
        println!("```");
    }
}

/// Given a connection to a database, output a GitHub-Flavored Markdown table of operators
fn output_operators(connection: &mut Client) {
    println!("## Operators");
    let results = connection.query(OP_QUERY, &[]).unwrap();
    for row in results {
        let schema: &str = row.get("schema");
        let name: &str = row.get("name");
        let left_arg_type: &str = row.get("left_arg_type");
        let right_arg_type: &str = row.get("right_arg_type");
        let result_type: &str = row.get("result_type");
        println!(
            "### {} {} {} → {}",
            left_arg_type, name, right_arg_type, result_type
        );
        let function: &str = row.get("function");
        let description: &str = row.get("description");
        println!("{}", description);
        println!();
        println!("__Function:__ {}", function);
        println!();
        println!("__Schema:__ {}", schema);
    }
}
